1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 import java.util.*;
33 import java.security.AlgorithmParameters;
34 import javax.crypto.*;
35 import javax.crypto.spec.*;
36
37 public class CTSMode {
38
39 private final static byte[] toByteArray(String s) {
40 char[] c = s.toCharArray();
41 byte[] t = new byte[c.length / 2];
42 int n = 0;
43 int d1 = -1;
44 int d2 = -1;
45 for (int i = 0; i < c.length; i++) {
46 char ch = c[i];
47 if (d1 == -1) {
48 d1 = Character.digit(ch, 16);
49 } else {
50 d2 = Character.digit(ch, 16);
51 if (d2 != -1) {
52 t[n++] = (byte)((d1 << 4) | d2);
53 d1 = -1;
54 d2 = -1;
55 }
56 }
57 }
58 if (d1 != -1) {
59 throw new RuntimeException();
60 }
61 if (n == t.length) {
62 return t;
63 }
64 byte[] b = new byte[n];
65 System.arraycopy(t, 0, b, 0, n);
66 return b;
67 }
68
69 private final static SecretKey KEY1 =
70 new SecretKeySpec(toByteArray("636869636b656e207465726979616b69"),
71 "AES");
72
73 private final static IvParameterSpec IV1 =
74 new IvParameterSpec(new byte[16]);
75
76
77
78
79
80
81 private final static byte[][] PLAIN1 = {
82 toByteArray("4920776f756c64206c696b652074686520"),
83 toByteArray("4920776f756c64206c696b6520746865" +
84 "2047656e6572616c20476175277320"),
85 toByteArray("4920776f756c64206c696b6520746865" +
86 "2047656e6572616c2047617527732043"),
87 toByteArray("4920776f756c64206c696b6520746865" +
88 "2047656e6572616c2047617527732043" +
89 "6869636b656e2c20706c656173652c"),
90 toByteArray("4920776f756c64206c696b6520746865" +
91 "2047656e6572616c2047617527732043" +
92 "6869636b656e2c20706c656173652c20"),
93 toByteArray("4920776f756c64206c696b6520746865" +
94 "2047656e6572616c2047617527732043" +
95 "6869636b656e2c20706c656173652c20" +
96 "616e6420776f6e746f6e20736f75702e")
97 };
98 private final static byte[][] CIPHER1 = {
99 toByteArray("c6353568f2bf8cb4d8a580362da7ff7f97"),
100 toByteArray("fc00783e0efdb2c1d445d4c8eff7ed22" +
101 "97687268d6ecccc0c07b25e25ecfe5"),
102 toByteArray("39312523a78662d5be7fcbcc98ebf5a8" +
103 "97687268d6ecccc0c07b25e25ecfe584"),
104 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
105 "b3fffd940c16a18c1b5549d2f838029e" +
106 "39312523a78662d5be7fcbcc98ebf5"),
107 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
108 "9dad8bbb96c4cdc03bc103e1a194bbd8" +
109 "39312523a78662d5be7fcbcc98ebf5a8"),
110 toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
111 "39312523a78662d5be7fcbcc98ebf5a8" +
112 "4807efe836ee89a526730dbc2f7bc840" +
113 "9dad8bbb96c4cdc03bc103e1a194bbd8"),
114 };
115
116 private final static byte[] IV2_SRC =
117 toByteArray("11223344556677880011223344556677");
118
119 private final static String[] ALGORITHMS2 = {
120 "DES", "DESede", "Blowfish", "AES"
121 };
122 private final static int[] KEYSIZES2 = {
123 8, 24, 16, 16
124 };
125
126 private static String toString(byte[] b) {
127 StringBuffer sb = new StringBuffer(b.length * 3);
128 for (int i = 0; i < b.length; i++) {
129 int k = b[i] & 0xff;
130 if (i != 0) {
131 sb.append(':');
132 }
133 sb.append(Character.forDigit(k >> 4, 16));
134 sb.append(Character.forDigit(k & 0xf, 16));
135 }
136 return sb.toString();
137 }
138
139 public static void main(String[] args) throws Exception {
140 test1();
141 test2();
142 test3();
143 }
144
145
146
147
148 private static void test1() throws Exception {
149 for (int i = 0; i < PLAIN1.length; i++) {
150 String algo = KEY1.getAlgorithm();
151 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
152 if (KEY1.getEncoded().length > MAX_KEYSIZE) {
153
154
155 continue;
156 }
157 System.out.println("Running test1_" + i + " (" + algo + ")");
158 Cipher cipher = Cipher.getInstance(algo+ "/CTS/NoPadding",
159 "SunJCE");
160 byte[] plainText = PLAIN1[i];
161 byte[] cipherText = CIPHER1[i];
162 cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
163 byte[] enc = cipher.doFinal(plainText);
164 if (Arrays.equals(cipherText, enc) == false) {
165 System.out.println("plain: " + toString(plainText));
166 System.out.println("cipher: " + toString(cipherText));
167 System.out.println("actual: " + toString(enc));
168 throw new RuntimeException("Encryption failure for test " + i);
169 }
170 cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
171 byte[] dec = cipher.doFinal(cipherText);
172 if (Arrays.equals(plainText, dec) == false) {
173 System.out.println("cipher: " + toString(cipherText));
174 System.out.println("plain: " + toString(plainText));
175 System.out.println("actual: " + toString(enc));
176 throw new RuntimeException("Decryption failure for test " + i);
177 }
178 }
179 }
180
181
182
183
184
185 private static void test2() throws Exception {
186 for (int i = 0; i < ALGORITHMS2.length; i++) {
187 String algo = ALGORITHMS2[i];
188 System.out.println("Running test2_" + i + " (" + algo + ")");
189 int keySize = KEYSIZES2[i];
190 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
191 if (keySize > MAX_KEYSIZE) {
192
193
194 continue;
195 }
196 Cipher cipher =
197 Cipher.getInstance(algo+"/CTS/NoPadding", "SunJCE");
198 int blockSize = cipher.getBlockSize();
199 SecretKeySpec key = new SecretKeySpec(new byte[keySize], algo);
200
201 byte[] plainText = PLAIN1[3];
202 cipher.init(Cipher.ENCRYPT_MODE, key);
203 byte[] cipherText = new byte[plainText.length];
204 int firstPartLen = blockSize + 1;
205 int processed1 = cipher.update(plainText, 0, firstPartLen,
206 cipherText, 0);
207 int processed2 = cipher.doFinal(plainText, firstPartLen,
208 plainText.length-firstPartLen,
209 cipherText, processed1);
210 AlgorithmParameters params = cipher.getParameters();
211 if ((processed1 + processed2) != plainText.length) {
212 System.out.println("processed1 = " + processed1);
213 System.out.println("processed2 = " + processed2);
214 System.out.println("total length = " + plainText.length);
215 throw new RuntimeException("Encryption failure for test " + i);
216 }
217
218
219 try {
220 cipher.doFinal(new byte[blockSize-1]);
221 throw new RuntimeException("Expected IBSE is not thrown");
222 } catch (IllegalBlockSizeException ibse) {
223 }
224
225
226 IvParameterSpec iv2 = new IvParameterSpec(IV2_SRC, 0, blockSize);
227 cipher.init(Cipher.ENCRYPT_MODE, key, iv2);
228 Cipher cipher2 =
229 Cipher.getInstance(algo+"/CBC/NoPadding", "SunJCE");
230 cipher2.init(Cipher.ENCRYPT_MODE, key, iv2);
231
232 byte[] eout = cipher.doFinal(IV2_SRC, 0, blockSize);
233 byte[] eout2 = cipher2.doFinal(IV2_SRC, 0, blockSize);
234 if (!Arrays.equals(eout, eout2)) {
235 throw new RuntimeException("Different encryption output " +
236 "for CBC and CTS");
237 }
238
239 cipher.init(Cipher.DECRYPT_MODE, key, params);
240 byte[] recoveredText =
241 new byte[cipher.getOutputSize(cipherText.length)];
242 processed1 = cipher.update(cipherText, 0, firstPartLen,
243 recoveredText, 0);
244 processed2 = cipher.update(cipherText, firstPartLen,
245 cipherText.length-firstPartLen,
246 recoveredText, processed1);
247 int processed3 =
248 cipher.doFinal(recoveredText, processed1+processed2);
249 if ((processed1 + processed2 + processed3) != plainText.length) {
250 System.out.println("processed1 = " + processed1);
251 System.out.println("processed2 = " + processed2);
252 System.out.println("processed3 = " + processed3);
253 System.out.println("total length = " + plainText.length);
254 throw new RuntimeException("Decryption failure for test " + i);
255 }
256 if (Arrays.equals(plainText, recoveredText) == false) {
257 System.out.println("plain: " + toString(plainText));
258 System.out.println("recovered: " + toString(recoveredText));
259 throw new RuntimeException("Decryption failure for test " + i);
260 }
261
262
263 try {
264 cipher.doFinal(new byte[blockSize-1]);
265 throw new RuntimeException("Expected IBSE is not thrown");
266 } catch (IllegalBlockSizeException ibse) {
267 }
268
269
270 cipher.init(Cipher.DECRYPT_MODE, key, iv2);
271 cipher2.init(Cipher.DECRYPT_MODE, key, iv2);
272 byte[] dout = cipher.doFinal(eout);
273 byte[] dout2 = cipher2.doFinal(eout2);
274 if (!Arrays.equals(dout, dout2)) {
275 throw new RuntimeException("Different decryption output " +
276 "for CBC and CTS");
277 }
278 }
279 }
280
281
282
283
284
285 private static void test3() throws Exception {
286
287 for (int i = 3; i < PLAIN1.length; i++) {
288 String algo = KEY1.getAlgorithm();
289 System.out.println("Running test3_" + i + " (" + algo + ")");
290 int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
291 if (KEY1.getEncoded().length > MAX_KEYSIZE) {
292
293
294 continue;
295 }
296 Cipher cipher =
297 Cipher.getInstance(algo+ "/CTS/NoPadding", "SunJCE");
298 byte[] plainText = PLAIN1[i];
299 byte[] cipherText = CIPHER1[i];
300 byte[] enc = new byte[plainText.length];
301 cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
302 int halfInput = plainText.length/2;
303 int processed1 = cipher.update(plainText, 0, halfInput,
304 enc, 0);
305 try {
306 cipher.doFinal(plainText, halfInput,
307 plainText.length-halfInput,
308 new byte[1], 0);
309 throw new RuntimeException("Expected Exception is not thrown");
310 } catch (ShortBufferException sbe) {
311
312 int processed2 =
313 cipher.doFinal(plainText, halfInput,
314 plainText.length-halfInput,
315 enc, processed1);
316 if ((processed1 + processed2) != enc.length) {
317 System.out.println("processed1 = " + processed1);
318 System.out.println("processed2 = " + processed2);
319 System.out.println("total length = " + enc.length);
320 throw new RuntimeException("Encryption length check " +
321 "failed");
322 }
323 }
324 if (Arrays.equals(cipherText, enc) == false) {
325 System.out.println("plain: " + toString(plainText));
326 System.out.println("cipher: " + toString(cipherText));
327 System.out.println("actual: " + toString(enc));
328 throw new RuntimeException("Encryption failure for test " + i);
329 }
330 cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
331 byte[] dec =
332 new byte[cipher.getOutputSize(cipherText.length)];
333 processed1 = cipher.update(cipherText, 0, halfInput,
334 dec, 0);
335 try {
336 cipher.update(cipherText, halfInput,
337 cipherText.length-halfInput,
338 new byte[1], 0);
339 throw new RuntimeException("Expected Exception is not thrown");
340 } catch (ShortBufferException sbe) {
341
342 int processed2 = cipher.update(cipherText, halfInput,
343 cipherText.length-halfInput,
344 dec, processed1);
345 int processed3 =
346 cipher.doFinal(dec, processed1+processed2);
347 if ((processed1 + processed2 + processed3) != dec.length) {
348 System.out.println("processed1 = " + processed1);
349 System.out.println("processed2 = " + processed2);
350 System.out.println("processed3 = " + processed3);
351 System.out.println("total length = " + dec.length);
352 throw new RuntimeException("Decryption length check " +
353 "failed");
354 }
355 }
356 if (Arrays.equals(plainText, dec) == false) {
357 System.out.println("cipher: " + toString(cipherText));
358 System.out.println("plain: " + toString(plainText));
359 System.out.println("actualD: " + toString(dec));
360 throw new RuntimeException("Decryption failure for test " + i);
361 }
362 }
363 }
364 }